package com.eva.framework.security;

import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONObject;
import com.eva.framework.common.constants.ResponseStatus;
import com.eva.framework.common.exception.BusinessException;
import com.eva.framework.common.model.ApiResponse;
import com.eva.framework.common.servlet.rewrite.ServletDuplicateInputStream;
import com.eva.framework.common.servlet.rewrite.ServletDuplicateRequestWrapper;
import com.eva.framework.security.utils.SecurityUtil;
import jakarta.servlet.http.HttpServletResponse;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;
import org.springframework.stereotype.Component;

import jakarta.annotation.Resource;
import jakarta.servlet.*;
import jakarta.servlet.annotation.WebFilter;
import jakarta.servlet.http.HttpServletRequest;
import java.io.IOException;

/**
 * 请求参数过滤器，实现参数的解密和重新注入
 */
@Slf4j
@Component
@WebFilter(urlPatterns = "/*", filterName = "requestParamFilter")
public class SecureRequestFilter implements Filter {

    @Resource
    private SecureProperties secureProperties;

    @Override
    public void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain filterChain) throws IOException, ServletException {
        try {
            // 不存在加密接口，直接放行
            String[] pathPatterns = secureProperties.getTransmission().getPathPatterns();
            if (pathPatterns == null || pathPatterns.length == 0) {
                filterChain.doFilter(servletRequest, servletResponse);
                return;
            }
            HttpServletRequest httpServletRequest = (HttpServletRequest) servletRequest;
            String uri = httpServletRequest.getRequestURI().replace(httpServletRequest.getContextPath(), "");
            // 解密请求参数
            if (SecurityUtil.isSecureRequest(httpServletRequest)) {
                // 防止请求流被拦截器进行一次读取后关闭导致流无法连续读取的问题
                ServletDuplicateRequestWrapper requestWrapper = new ServletDuplicateRequestWrapper(httpServletRequest);
                ServletDuplicateInputStream duplicateInputStream = ((ServletDuplicateInputStream)requestWrapper.getInputStream());
                // 解密URL参数
                String requestParameter = servletRequest.getParameter("_p");
                if (StringUtils.isNotBlank(requestParameter)) {
                    try {
                        requestParameter = SecurityUtil.decryptTransmission(requestParameter);
                        JSONObject paramsJson = JSON.parseObject(requestParameter);
                        for (String key : paramsJson.keySet()) {
                            requestWrapper.addParameter(key, paramsJson.get(key));
                        }
                    } catch (SecurityException e) {
                        throw new BusinessException(ResponseStatus.BAD_REQUEST, "解密接口 " + uri + " 失败！", e);
                    }
                }
                // 解密body参数
                String body = duplicateInputStream.getBody();
                if (StringUtils.isNotBlank(body)) {
                    JSONObject jsonBody;
                    try {
                        jsonBody = JSON.parseObject(body);
                    } catch (Exception e) {
                        throw new BusinessException(ResponseStatus.BAD_REQUEST, "解密接口 " + uri + " 失败，接口参数需为J正确的SON字符串！");
                    }
                    String requestBody = jsonBody.getString("_p");
                    // 没有加密参数
                    if (jsonBody.keySet().size() > 0 && StringUtils.isBlank(requestBody)) {
                        throw new BusinessException(ResponseStatus.BAD_REQUEST, "解密接口 " + uri + " 失败，接口需传递_p参数！");
                    }
                    try {
                        duplicateInputStream.setBody(SecurityUtil.decryptTransmission(requestBody));
                    } catch (SecurityException e) {
                        throw new BusinessException(ResponseStatus.BAD_REQUEST, "解密接口 " + uri + " 失败！请检查前后端密钥配置是否一致。", e);
                    }
                }
                filterChain.doFilter(requestWrapper, servletResponse);
                return;
            }
            filterChain.doFilter(servletRequest, servletResponse);
        } catch (BusinessException e) {
            // 出现业务异常时，直接做出响应
            ApiResponse.response((HttpServletResponse) servletResponse, ApiResponse.failed(e.getCode(), e.getMessage()));
        }
    }
}
